package redis

type key struct {
	c    *Client
	key  string
	keys []string
}

func (k *key) cmd(name string, args ...any) *cmd {
	return &cmd{c: k.c, cmd: name, args: args}
}

type Caster interface {
	String() StringKey
	Hash() HashKey
	List() ListKey
	Set() SetKey
	Zset() ZsetKey
	HyperLogLog() HyperLogLogKey
	Bit() BitKey
	Key() KeyKey
	Script() ScriptKey
}

type caster struct {
	k *key
}

func Cast(k any) Caster {
	kk, ok := k.(*key)
	if !ok {
		panic("Cast only accept type key")
	}
	return &caster{kk}
}

// String

type StringKey interface {
	Set(value any) ValueResult
	Get() ValueResult
	GetDel() ValueResult
	GetEx(*GetArgs) ValueResult
	GetSet(value any) ValueResult
	StrLen() IntResult
	Append(value string) IntResult
	SetRange(offset int, value string) IntResult
	GetRange(start, end int) StringResult
	Incr() IntResult
	IncrBy(increment int) IntResult
	IncrByFloat(increment float64) FloatResult
	Decr() IntResult
	DecrBy(increment int) IntResult
	MSet(value ...any) BoolResult
	MSetNx(value ...any) BoolResult
	MGet() ListResult
}

func (c *Client) String(key ...string) StringKey {
	return c.key(key)
}

func (c *caster) String() StringKey {
	return c.k
}

// Hash

type HashKey interface {
	HSet(field string, value any) BoolResult
	HSetNX(field string, value any) BoolResult
	HGet(field string) ValueResult
	HExists(field string) BoolResult
	HDel(field ...string) IntResult
	HLen() IntResult
	HStrLen(field string) IntResult
	HIncrBy(field string, increment int) IntResult
	HIncrByFloat(field string, increment float64) FloatResult
	HMSet(fieldValues map[string]any) BoolResult
	HMGet(field ...string) ListResult
	HKeys() StringListResult
	HVals() ListResult
	HGetAll() MapResult
	HScan(args *ScanArgs) error
}

func (c *Client) Hash(key ...string) HashKey {
	return c.key(key)
}

func (c *caster) Hash() HashKey {
	return c.k
}

// List

type ListKey interface {
	LPush(value ...any) IntResult
	LPushX(value any) IntResult
	RPush(value ...any) IntResult
	RPushX(value any) IntResult
	LPop() ValueResult
	RPop() ValueResult
	LRem(count int, value any) IntResult
	LLen() IntResult
	LIndex(index int) ValueResult
	LInsertBefore(pivot, value any) IntResult
	LInsertAfter(pivot, value any) IntResult
	LSet(index int, value any) BoolResult
	LRange(start, stop int) ListResult
	LTrim(start, stop int) ListResult
	BLPop(timeout int) ListResult
	BRPop(timeout int) ListResult
	LMove(srcDir, dstDir ListDirection) ValueResult
	BLMove(srcDir, dstDir ListDirection, timeout int) ValueResult
	LPos(elem any, ex *LPosArgs) ValueResult
	LPosCount(elem any, cnt int, ex *LPosArgs) ListResult
}

func (c *Client) List(key ...string) ListKey {
	return c.key(key)
}

func (c *caster) List() ListKey {
	return c.k
}

// Set

type SetKey interface {
	SAdd(member ...any) IntResult
	SIsMember(member any) BoolResult
	SMIsMember(member ...any) IntListResult
	SPop() ValueResult
	SRandMember(count ...int) ValueResult
	SRem(member ...any) IntResult
	SMove(member any) BoolResult
	SCard() IntResult
	SMembers() ListResult
	SInter() ListResult
	SInterStore() IntResult
	SUnion() ListResult
	SUnionStore() IntResult
	SDiff() ListResult
	SDiffStore() IntResult
	SScan(args *ScanArgs) error
}

func (c *Client) Set(key ...string) SetKey {
	return c.key(key)
}

func (c *caster) Set() SetKey {
	return c.k
}

// Zset

type ZsetKey interface {
	ZAdd(data ...*ZData) IntResult
	ZScore(member any) IntResult
	ZIncrBy(data *ZData) IntResult
	ZCard() IntResult
	ZCount(min, max int64) IntResult
	ZRange(start, stop int) ListResult
	ZRangeWithScores(start, stop int) ZRangeResult
	ZRevRange(start, stop int) ListResult
	ZRevRangeWithScores(start, stop int) ZRangeResult
	ZRangeByScore(min, max int64) ListResult
	ZRangeByScoreWithScores(min, max int64) ZRangeResult
	ZRangeByScoreWithLimit(min, max int64, limitOffset, limitCount int) ListResult
	ZRangeByScoreWithScoresLimit(min, max int64, limitOffset, limitCount int) ZRangeResult
	ZRevRangeByScore(min, max int64) ListResult
	ZRevRangeByScoreWithScores(min, max int64) ZRangeResult
	ZRevRangeByScoreWithLimit(min, max int64, limitOffset, limitCount int) ListResult
	ZRevRangeByScoreWithScoresLimit(min, max int64, limitOffset, limitCount int) ZRangeResult
	ZRank(member any) IntResult
	ZRevRank(member any) IntResult
	ZRem(member ...any) IntResult
	ZRemRangeByRank(start, stop int) IntResult
	ZRemRangeByScore(min, max int64) IntResult
	ZRangeByLex(min, max int64) ListResult
	ZRangeByLexWithLimit(min, max int64, limitOffset, limitCount int) ListResult
	ZLexCount(min, max int64) IntResult
	ZRemRangeByLex(min, max int64) IntResult
	ZUnionStore(args *ZAggArgs) IntResult
	ZInterStore(args *ZAggArgs) IntResult
	ZDiffStore() IntResult
	ZScan(args *ScanArgs) error
	ZPopMin(count int) ZRangeResult
	ZPopMax(count int) ZRangeResult
	BZPopMin(timeout int) ListResult
	BZPopMax(timeout int) ListResult
}

func (c *Client) Zset(key ...string) ZsetKey {
	return c.key(key)
}

func (c *caster) Zset() ZsetKey {
	return c.k
}

// HyperLogLog

type HyperLogLogKey interface {
	PfAdd(elem ...any) BoolResult
	PfCount() IntResult
	PfMerge() BoolResult
}

func (c *Client) HyperLogLog(key ...string) HyperLogLogKey {
	return c.key(key)
}

func (c *caster) HyperLogLog() HyperLogLogKey {
	return c.k
}

// Bit

type BitKey interface {
	SetBit(offset, value int) IntResult
	GetBit(offset int) IntResult
	BitCount() IntResult
	BitCountRange(start, end int) IntResult
	BitPos(bit int) IntResult
	BitPosRange(bit, start, end int) IntResult
	BitOp(op string) IntResult
}

func (c *Client) Bit(key ...string) BitKey {
	return c.key(key)
}

func (c *caster) Bit() BitKey {
	return c.k
}

// Key

type KeyKey interface {
	Exists() BoolResult
	Type() StringResult
	Rename(newKey string) BoolResult
	RenameNx(newKey string) BoolResult
	Move(db int) BoolResult
	Del() IntResult
	Unlink() IntResult
	Expire(seconds int) BoolResult
	ExpireAt(timestamp int) BoolResult
	TTL() IntResult
	Persist() BoolResult
	PExpire(milliseconds int) BoolResult
	PExpireAt(timestamp_ms int) BoolResult
	PTTL() IntResult
}

func (c *Client) Key(key ...string) KeyKey {
	return c.key(key)
}

func (c *caster) Key() KeyKey {
	return c.k
}

// Script

type ScriptKey interface {
	Eval(script *Script, args ...any) Result
}

func (c *Client) Script(key ...string) ScriptKey {
	return c.key(key)
}

func (c *caster) Script() ScriptKey {
	return c.k
}

// Server

type ServerKey interface {
	Multi(cmds ...any) ([]Result, error)
	Scan(args *ScanArgs) error
	Keys(pattern string) StringListResult
}

func (c *Client) Server() ServerKey {
	return c.key(nil)
}

// PubSub

type PubSubKey interface {
	Publish(channel string, value any) IntResult
	Subscriber() *Subscriber
}

func (c *Client) PubSub() PubSubKey {
	return c.key(nil)
}

func (c *caster) PubSub() PubSubKey {
	return c.k
}
